package com.antoco.controlcenter.comm.commLink;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.antoco.controlcenter.base.BaseApplication;
import com.antoco.controlcenter.comm.message.RobotMessageBase;
import com.antoco.controlcenter.comm.codec.Byte2MessageDecoder;
import com.antoco.controlcenter.event.DataRefreshEventEntity;
import com.antoco.controlcenter.iface.IMoveEventListener;
import com.antoco.controlcenter.utils.StringUtils;
import com.antoco.controlcenter.utils.ToastUtils;

import org.greenrobot.eventbus.EventBus;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.List;

import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.observers.DisposableObserver;
import io.reactivex.schedulers.Schedulers;

public class CommLinkTcp implements ICommLink, IMoveEventListener {
    String TAG_log = "CommLink";


    private Socket mSocket;

    private OutputStream mOutputStream = null;
    private InputStream mInputStream = null;

    private Byte2MessageDecoder decoder; //解码器
    private CompositeDisposable mCompositeDisposable;
    private ICommEventListener onCommEventListener = null;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    starReadRunable();
                    ToastUtils.showToast(BaseApplication.getContext(), "通信连接成功");
                    break;
            }
        }
    };

    public CommLinkTcp() {
        decoder = new Byte2MessageDecoder();
        mCompositeDisposable = new CompositeDisposable();
    }


    @Override
    public boolean isConnected() {
        boolean flag = false;
        if (mSocket != null) {
            flag = mSocket.isConnected();
        }
        return flag;
    }

    @Override
    public boolean dataTransmit(byte[] buffer) {
        return false;
    }

    @Override
    public boolean FrameTransmit(Object frame) {
        //TODO:帧发送逻辑
        return false;
    }

    public void startSocketRunable(String ip, int port) {
        Observable<Long> observable = Observable.interval(0, 500, TimeUnit.MILLISECONDS).take(3).doOnNext(aLong -> socketRunable(ip, port));
        DisposableObserver<Long> disposableObserver = getDisposableObserver();
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
        mCompositeDisposable.add(disposableObserver);
    }

    private DisposableObserver<Long> getDisposableObserver() {

        return new DisposableObserver<Long>() {

            @Override
            public void onNext(Long aLong) {
            }

            @Override
            public void onError(Throwable throwable) {
            }

            @Override
            public void onComplete() {
            }
        };
    }


    public void clear() {
        mHandler.removeCallbacksAndMessages(null);
        if (null == mCompositeDisposable) {
            return;
        }
        mCompositeDisposable.clear();
    }

    private void socketRunable(final String ip, final int port) {

        try {
            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }

            InetAddress ipAddress = InetAddress.getByName(ip);
            mSocket = new Socket(ipAddress, port);
            //设置不延时发送
            mSocket.setTcpNoDelay(true);
            //设置输入输出缓冲流大小
            mSocket.setSendBufferSize(8 * 1024);
            mSocket.setReceiveBufferSize(8 * 1024);
            if (isConnect()) {
                mOutputStream = null;
                mInputStream = null;
                mOutputStream = mSocket.getOutputStream();
                mInputStream = mSocket.getInputStream();
                mHandler.sendEmptyMessage(1); //通信启动通知
        }
        } catch (IOException e) {
            // mHandler.sendMessage(Message.obtain(mHandler, -1, "通信连接失败"));
            Log.e(TAG_log, "communication connect io exception = " + e.getMessage());
            e.printStackTrace();
            return;
        }


    }

    public void starReadRunable() {
        Observable<Long> observable = Observable.interval(0, 500, TimeUnit.MILLISECONDS).doOnNext(aLong -> readMessage());
        DisposableObserver<Long> disposableObserver = getDisposableObserver();
        observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
        mCompositeDisposable.add(disposableObserver);
    }

    private void readMessage() {
        try {
            if (mInputStream == null) return;
            byte[] buffer = new byte[mInputStream.available()];
            int size = mInputStream.read(buffer);//null data -1 , zrd serial rule size default 10
            if (size == -1) {
            }
            if (size > 0) {
                List<RobotMessageBase> frameList = decoder.decode(buffer);
                if (frameList != null) {
                    for (RobotMessageBase message : frameList) {
                        onCommEventListener.onDataReceive(message);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;

        }
    }


    //==============================socket connect============================

    /**
     * connect socket in thread
     * Exception : android.os.NetworkOnMainThreadException
     */
    @Override
    public boolean connect(String commString) {
        if (!StringUtils.isEmpty(commString)) {
            String[] param = commString.split("\\:");
            if (param.length > 1) {
                startSocketRunable(param[0], Integer.parseInt(param[1]));
                return true;
            }
        } else {
            Log.d("TAG", "通信连接失败");
        }
        return false;
    }

    /**
     * socket is connect
     */
    public boolean isConnect() {
        boolean flag = false;
        if (mSocket != null) {
            flag = mSocket.isConnected();
        }
        return flag;
    }

    /**
     * socket disconnect
     */
    @Override
    public boolean disConnect() {
        clear();
        try {
            if (mOutputStream != null) {
                mOutputStream.close();
            }

            if (mInputStream != null) {
                mInputStream.close();
            }

            if (mSocket != null) {
                mSocket.close();
                mSocket = null;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }


    public void sendByteCmd(final byte[] mBuffer) {
        if (isConnect() && mOutputStream != null) {
            try {
                if (mOutputStream != null) {
                    mOutputStream.write(mBuffer);
                    mOutputStream.flush();
                }
            } catch (IOException e) {
                disConnect();
                e.printStackTrace();
            }

        }
    }


    /**
     * send string cmd to serial
     */
    public void sendStrCmds(String cmd) {
        byte[] mBuffer = cmd.getBytes();
        sendByteCmd(mBuffer);
    }


    /**
     * send prt content cmd to serial
     */
    public void sendChsPrtCmds(String content) {
        try {
            byte[] mBuffer = content.getBytes("GB2312");
            sendByteCmd(mBuffer);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    /**
     * 数据接收回调
     *
     * @param commEventListener
     */
    public void setCommEventListener(
            ICommEventListener commEventListener) {
        onCommEventListener = commEventListener;
    }

    @Override
    public void onMoveChange(byte[] buffer) {
        sendByteCmd(buffer);
        EventBus.getDefault().postSticky(new DataRefreshEventEntity());
    }

    public IMoveEventListener getMoveEventListener() {
        return this;
    }
}
